package com.fezs.security.utils;

import cn.hutool.core.util.StrUtil;
import com.fezs.common.constants.CacheConstant;
import com.fezs.common.context.BaseContextHandler;
import com.fezs.common.model.LoginUser;
import com.fezs.redis.utils.RedisUtils;
import com.fezs.web.mvc.utils.JwtUtils;
import com.fezs.web.mvc.utils.WebServletUtils;
import io.jsonwebtoken.Claims;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;

import javax.servlet.http.HttpServletRequest;
import java.time.Duration;
import java.util.Collections;
import java.util.Set;

/**
 * 安全服务工具类
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class SecurityUtils {

    public static final String AUTHORIZATION_BEARER = "Bearer";
    public static final String AUTHORIZATION = "Authorization";

    /**
     * 获取用户ID
     */
    public static Long getUserId() {
        Long userId = BaseContextHandler.getUserId();
        if (userId == null || userId == 0L) {
            throw new AuthenticationServiceException("未登录!");
        }
        return userId;
    }

    /**
     * 获取登录用户信息
     */
    public static LoginUser getLoginUser() {
        LoginUser loginUser = BaseContextHandler.getLoginUser();
        if (loginUser == null) {
            throw new AuthenticationServiceException("未登录!");
        }
        return loginUser;
    }

    /**
     * 获取登录用户信息
     */
    public static LoginUser getLoginUserFromToken(HttpServletRequest request, String tokenName) {
        String token = getToken(request, tokenName);
        if (StrUtil.isEmpty(token)) {
            return null;
        }
        Claims claims = JwtUtils.parseToken(token);
        Long userId = JwtUtils.getUserId(claims);
        if (userId == null) {
            return null;
        }
        String deviceType = JwtUtils.getDeviceType(claims);
        return getLoginUserByCache(userId, deviceType);
    }

    /**
     * 获取登录用户信息
     */
    public static LoginUser getLoginUserByCache(Long userId, String deviceType) {
        return RedisUtils.getCacheObject(CacheConstant.contact(CacheConstant.LOGIN_USER, userId, deviceType));
    }

    /**
     * 设置登录用户信息 - 在登录接口中使用
     */
    public static void setLoginUserByCache(LoginUser loginUser, Duration duration) {
        String logninUserKey = CacheConstant.contact(CacheConstant.LOGIN_USER, loginUser.getUserId(), loginUser.getDeviceType());
        RedisUtils.setCacheObject(logninUserKey, loginUser, duration);
    }

    /**
     * 设置用户权限点信息 - 在登录接口中使用
     */
    public static void setUserPermissionByCache(Long userId, Set<String> permissions, Duration duration) {
        String key = CacheConstant.contact(CacheConstant.USER_PERMISSION, userId);
        RedisUtils.setCacheSet(key, permissions, duration);
    }

    /**
     * 设置用户角色信息 - 在登录接口中使用
     */
    public static void setUserRoleByCache(Long userId, Set<String> roles, Duration duration) {
        String key = CacheConstant.contact(CacheConstant.USER_ROLE, userId);
        RedisUtils.setCacheSet(key, roles, duration);
    }

    /**
     * 获取用户权限点信息
     */
    public static Set<String> getUserPermissionByCache(Long userId) {
        String key = CacheConstant.contact(CacheConstant.USER_PERMISSION, userId);
        return RedisUtils.getCacheSet(key);
    }

    /**
     * 获取用户角色信息
     */
    public static Set<String> getUserRoleByCache(Long userId) {
        String key = CacheConstant.contact(CacheConstant.USER_ROLE, userId);
        return RedisUtils.getCacheSet(key);
    }

    /**
     * 获取请求token
     */
    public static String getToken() {
        return getToken(WebServletUtils.getRequest(), AUTHORIZATION);
    }

    /**
     * 获取请求token
     */
    public static String getToken(String tokenName) {
        return getToken(WebServletUtils.getRequest(), tokenName);
    }

    /**
     * 根据request获取请求token
     */
    public static String getToken(HttpServletRequest request, String tokenName) {
        if (request == null) {
            return null;
        }
        // 从header获取token标识
        String token = request.getHeader(tokenName);
        return replaceTokenPrefix(token);
    }

    /**
     * 裁剪token前缀
     */
    private static String replaceTokenPrefix(String token) {
        // 如果前端设置了令牌前缀，则裁剪掉前缀
        if (StrUtil.isNotEmpty(token) && token.startsWith(AUTHORIZATION_BEARER)) {
            token = StrUtil.trim(token.replaceFirst(AUTHORIZATION_BEARER, ""));
        }
        return token;
    }

    /**
     * 是否为管理员
     *
     * @param userId 用户ID
     * @return 结果
     */
    public static boolean isAdmin(Long userId) {
        return userId != null && userId == 1L;
    }

    /**
     * 是否为管理员
     */
    public static boolean isAdmin() {
        LoginUser loginUser = getLoginUser();
        return loginUser.getUserId() == 1L;
    }

    /**
     * 设置当前用户
     *
     * @param loginUser 登录用户
     * @param request 请求
     */
    public static void setLoginUserAuth(LoginUser loginUser, HttpServletRequest request) {
        // 创建 Authentication, 并设置到上下文
        Authentication authentication = buildAuthentication(loginUser, request);
        SecurityContextHolder.getContext().setAuthentication(authentication);

        BaseContextHandler.setUserId(loginUser.getUserId());
        BaseContextHandler.setLoginUser(loginUser);
    }

    private static Authentication buildAuthentication(LoginUser loginUser, HttpServletRequest request) {
        // 创建 UsernamePasswordAuthenticationToken 对象
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
            loginUser, null, Collections.emptyList());
        authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        return authenticationToken;
    }

}
